/* 
 * lab1.cpp
 *
 * Template application for Procedural Shading lab.
 */

#include <math.h>
#include <assert.h>
#include <stdio.h>

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "ShaderSetup.h"

#define bool int
#define false 0
#define true 1

GLuint oneColorShader;
GLint oneColorShaderColor;
GLuint brickShader;
GLint brickShaderColor;
GLuint plasticShader;

int currentX;			// Current location of X
int currentY;			// Current location of Y
int startX;			// Starting location of X
int startY;			// Starting location of Y
int windowHeight;		// Current height of window
int windowWidth;		// Current width of window

float 	angle 		= 0.0; 
float	axis[3];
float 	lastPosition[3]	= { 0.0F, 0.0F, 0.0F };
float	trans[3];

bool 	redrawContinue 	= false;
bool    trackballMove 	= false;
bool 	trackingMouse 	= false;

// Scene will mimic the basic scene used in RenderMan exercises
//  Vertices and colors for drawing the objects in the scene
GLfloat WallColors[][3] = {
    { 1.0, 0.0, 0.0 }, { 1.0, 0.0, 0.0 }, { 1.0, 0.0, 0.0 }, 
    { 1.0, 0.0, 0.0 }
};

GLfloat WallVertices[][3] = {
    { -5.0,  5.0,  0.0 }, {  5.0, 5.0,  0.0 }, { 5.0,  -5.0,  0.0 }, 
    { -5.0,  -5.0,  0.0 }
};

GLfloat FloorColors[][3] = {
    { 1.0, 1.0, 0.5 }, { 1.0, 1.0, 0.5 }, { 1.0, 1.0, 0.5 }, 
    { 1.0, 1.0, 0.5 }
};

GLfloat FloorVertices[][3] = {
    { -5.0, -5.0,  5.0 }, {  5.0, -5.0,  5.0 }, { 5.0,  -5.0,  -5.0 }, 
    { -5.0, -5.0, -5.0 }
};

GLUquadricObj *sphere = NULL;


/*
 *  The function draws your scene.  It will also apply the shader and attach it to
 *  correct primitive.  For sake of the example, the shader will be applied to the
 *  wall.
 */

void drawscene( void ) {

	// draw your floor
    glUseProgram(brickShader);
    glUniform3f(brickShaderColor, 1, 1, 0);
    glBegin( GL_POLYGON );
	  glColor3fv(  FloorColors[0]   );
	  glVertex3fv( FloorVertices[0] );

	  glColor3fv(  FloorColors[1]   );
	  glVertex3fv( FloorVertices[1] );

	  glColor3fv(  FloorColors[2]   );
	  glVertex3fv( FloorVertices[2] );

	  glColor3fv(  FloorColors[3]   );
	  glVertex3fv( FloorVertices[3] );
	glEnd( );

    
	// draw the wall -- don't forget to assign your texture coords
	glUseProgram(oneColorShader);
    glUniform3f(oneColorShaderColor, 1, 0, 1);
    glBegin( GL_POLYGON );
	  glColor3fv(  WallColors[0]   );
	  glTexCoord2f (0.0, 0.0);
	  glVertex3fv( WallVertices[0] );

	  glColor3fv(  WallColors[1]   );
	  glTexCoord2f (0.0, 1.0);
	  glVertex3fv( WallVertices[1] );

	  glColor3fv(  WallColors[2]   );
	  glTexCoord2f (1.0, 1.0);
	  glVertex3fv( WallVertices[2] );

	  glColor3fv(  WallColors[3]   );
	  glTexCoord2f (1.0, 0.0);
	  glVertex3fv( WallVertices[3] );
	glEnd( );

	// draw the sphere
	if (!sphere) {
        glUseProgram(plasticShader);
		sphere = gluNewQuadric();
		gluQuadricDrawStyle(sphere, GLU_FILL);
	}

	glPushMatrix();
	  glTranslatef (0.0, 0.0, -2.0);
	  glColor3f (0.0, 0.0, 1.0);
	  gluSphere (sphere,2,100, 100);
	glPopMatrix();

}


#ifdef __cplusplus
extern "C" {
#endif

/*
 * Display callback function - used for redisplay as well
 */

void display( void ) {

	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// View transform 
	if ( trackballMove ) {
		glRotatef( angle, axis[0], axis[1], axis[2] );
	}

	drawscene();

	glutSwapBuffers();
}



/*
 * Callback function for screen window resizing/reshaping
 */

void myReshape( int width, int height ) {

	glViewport( 0, 0, width, height );
	windowWidth = width;
	windowHeight = height;

}

/*
 * Callback function for keyboard input
 *
 * Ignores all input except 'q', 'Q', and ESC
 */

void keyboard( unsigned char key, int x, int y ) {

	switch( key ) {

		case 'q': case 'Q': case 033:
			exit( EXIT_SUCCESS );
	
	}

}

#ifdef __cplusplus
}
#endif


GLuint shaderSetup(const char * vert, const char * frag)
{
	 GLuint errors;
	 GLuint x = ShaderSetup( vert, frag, &errors );
	 if( errors != E_NO_ERROR ) 
     {
		fprintf( stderr, "Error in shader setup - %s\n",
			 ErrorString(errors) );
		exit( 1 );
     }

     return x;
}



/*
 * OpenGL initialization
 */

void init( void ) {

	// enable depth testing
	glEnable( GL_DEPTH_TEST ); 
	glEnable (GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	// Camera stuff - matrix initialization
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );
	gluPerspective (45, 1.0, 1.0, 100.0);

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity( );
	gluLookAt (0.0, 15.0, -15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    oneColorShader = shaderSetup("oneColor.vert", "oneColor.frag");
    oneColorShaderColor = glGetUniformLocation(oneColorShader, "Color");
    brickShader = shaderSetup("brick2.vert", "brick2.frag");
    brickShaderColor = glGetUniformLocation(brickShader, "BrickColor");
    plasticShader = shaderSetup("plastic.vert", "plastic.frag");
}

/*
 * Main routine - GLUT setup and initialization
 */

int main( int argc, char **argv ) {

    // Initialize GLUT
	glutInit( &argc, argv );

	// Enable double buffering and depth buffering
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
	glutInitWindowSize( 500, 500 );
	glutCreateWindow( argv[0] );

	// Callback functions are specified 
	glutReshapeFunc( myReshape );
	glutDisplayFunc( display );
	glutKeyboardFunc( keyboard );

	// Do OpenGL initialization
	init();
	
	// enter your display loop.
	glutMainLoop();
	
	return 0;
}
